// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 开始：构建！aix&！达尔文&！蜻蜓&！freebsd&！linux&&！netbsd&！openbsd&！solaris 
// /+构建！aix，！达尔文，！蜻蜓，！freebsd，！linux，！netbsd，！openbsd，！solaris 

package os

import (
	"io"
	"runtime"
	"syscall"
)

func removeAll(path string) error {
	if path == "" {
		// 无法保持与RemoveAll先前行为
		// 的兼容性。见第28830期。
		return nil
	}

	// rmdir系统调用允许删除计划9中的“.”，
	// ，因此我们不允许它与“RemoveAll的实现”中的
	// ”保持一致。
	if endsWithDot(path) {
		return &PathError{Op: "RemoveAll", Path: path, Err: syscall.EINVAL}
	}

	// 简单的例子：如果删除有效，我们就完成了。
	err := Remove(path)
	if err == nil || IsNotExist(err) {
		return nil
	}

	// 否则，这是我们需要递归到的目录吗？
	dir, serr := Lstat(path)
	if serr != nil {
		if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
			return nil
		}
		return serr
	}
	if !dir.IsDir() {
		// 不是目录；从删除返回错误。
		return err
	}

	// 删除内容并返回第一个错误。
	err = nil
	for {
		fd, err := Open(path)
		if err != nil {
			if IsNotExist(err) {
				// 已被其他人删除。
				return nil
			}
			return err
		}

		const reqSize = 1024
		var names []string
		var readErr error

		for {
			numErr := 0
			names, readErr = fd.Readdirnames(reqSize)

			for _, name := range names {
				err1 := RemoveAll(path + string(PathSeparator) + name)
				if err == nil {
					err = err1
				}
				if err1 != nil {
					numErr++
				}
			}

			// 如果我们可以删除任何条目，请中断以开始新的迭代。
			// 否则，我们将放弃当前名称，获取下一个条目并尝试删除它们。
			if numErr != reqSize {
				break
			}
		}

		// 从目录中删除文件可能导致操作系统对其进行了重新洗牌。只需再次调用Readdirnames 
		// 可能会跳过一些条目。要避免这种情况，唯一可靠的方法是关闭并重新打开
		// 目录。见第20841期。
		fd.Close()

		if readErr == io.EOF {
			break
		}
		// 如果Readdirnames返回错误，请使用它。
		if err == nil {
			err = readErr
		}
		if len(names) == 0 {
			break
		}

		// 我们不想不必要地重新打开，因此如果我们从Readdirnames获得的请求名称少于，请尝试
		// 立即删除目录。如果
		// 成功，我们就完蛋了。
		if len(names) < reqSize {
			err1 := Remove(path)
			if err1 == nil || IsNotExist(err1) {
				return nil
			}

			if err != nil {
				// 删除
				// 目录内容时出错，由于我们读取的名称少于请求的名称
				// 可能没有更多的文件要删除。不要再次循环读取目录。我们可能会得到同样的错误。
				return err
			}
		}
	}

	// 删除目录。
	err1 := Remove(path)
	if err1 == nil || IsNotExist(err1) {
		return nil
	}
	if runtime.GOOS == "windows" && IsPermission(err1) {
		if fs, err := Stat(path); err == nil {
			if err = Chmod(path, FileMode(0200|int(fs.Mode()))); err == nil {
				err1 = Remove(path)
			}
		}
	}
	if err == nil {
		err = err1
	}
	return err
}
